#include "disk_info.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>

#if defined(__x86_64__) || defined(__aarch64__) || defined(_WIN64)
#define DISK_FSCANF(fp, disk)                                                        \
fscanf(fp, "%lu %lu %64s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))
#define DISK_SSCANF(buff, disk)                                                        \
sscanf(buff, "%lu %lu %64s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))

#elif defined(__i386__) || (defined(__arm__) && !defined(__aarch64__))
#define DISK_FSCANF(fp, disk)                                                        \
fscanf(fp, "%u %u %64s %u %u %u %u %u %u %u %u %u %u %u", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))
#define DISK_SSCANF(buff, disk)                                                        \
sscanf(buff, "%u %u %64s %u %u %u %u %u %u %u %u %u %u %u", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))

#else
#define DISK_FSCANF(fp, disk)                                                        \
fscanf(fp, "%u %u %64s %u %u %u %u %u %u %u %u %u %u %u", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))
#define DISK_SSCANF(buff, disk)                                                        \
sscanf(buff, "%u %u %64s %u %u %u %u %u %u %u %u %u %u %u", &((disk)->major_num), &((disk)->minor_num),        \
    ((disk)->device_name), &((disk)->read_complete), &((disk)->read_merge), &((disk)->read_sectors), &((disk)->read_time_ms), \
    &((disk)->write_complete), &((disk)->write_merge), &((disk)->write_sectors), &((disk)->write_time_ms), \
    &((disk)->io_current_in_process), &((disk)->io_time_ms), &((disk)->io_weighted_time_ms))
#endif
#define DISK_FSCANF_NUM 14
#define DISK_SSCANF_NUM 14
/*
struct statfs {
    long f_type;    // 文件系统类型 
    long f_bsize;   // 经过优化的传输块大小，单位B
    long f_blocks;  // 文件系统数据块总数 
    long f_bfree;   // 可用块数 
    long f_bavail;  // 非超级用户可获取的块数
    long f_files;   // 文件结点总数
    long f_ffree;   // 可用文件结点数
    fsid_t f_fsid;  // 文件系统标识 
    long f_namelen; // 文件名的最大长度 
};
*/

int GetDiskInfo(char *mounted, struct statfs *sfs) {
    if (!mounted || !sfs) return -1;
    // 参数： 文件系统的文件路径名（如 "/" , "/boot" 等）
    int ret = statfs(mounted, sfs);
    if (ret != 0) {
        perror("statfs");
        return -1;
    }
    return 0;
}

/*
struct mntent {
    char *mnt_fsname;   // 挂载的文件系统的名字
    char *mnt_dir;      // 挂载点
    char *mnt_type;     // 文件系统类型：ufs、nfs 等
    char *mnt_opts;     // 选项，以逗号为分隔符
    int mnt_freq;       // Dump 的频率(以天为单位)
    int mnt_passno;     // fsck检查的次序
};
*/

int GetMountedOnListNumber() {
    FILE *file;
    struct mntent *mountEntry;
    int cnt=0;

    file = setmntent("/etc/mtab", "r");
    if (file == NULL) {
        perror("Error opening /etc/mtab");
        return -1;
    }

    while ((mountEntry = getmntent(file)) != NULL) {
        cnt++;
        // printf("Mount point: %s\n", mountEntry->mnt_dir);
    }

    endmntent(file);

    return cnt;
}

int GetMountedOnList(int num, char *mounted) {
    if (num <= 0) return -1; 
    FILE *file;
    struct mntent *mountEntry;

    file = setmntent("/etc/mtab", "r");
    if (file == NULL) {
        perror("Error opening /etc/mtab");
        return -1;
    }

    while ((mountEntry = getmntent(file)) != NULL) {
        if (--num == 0) {
            strcpy(mounted, mountEntry->mnt_dir);
            endmntent(file);
            return 0;
        }
        // printf("Mount point: %s\n", mountEntry->mnt_dir);
    }

    endmntent(file);
    return -1;
}

static int IOUsageReadOnce(struct IoStatusInfo *info) {
    fflush(info->fp);
    if (fseek(info->fp, 0, SEEK_SET) != 0) {
        printf("fseek fail\n");
        return -1;
    }

    char buff[2048];
    for (int i = 0; i < info->disk_num; i++) {
        // printf("i = %d\n", i);
        if (fgets(buff, 2048, info->fp) == NULL) {
            printf("fgets fail\n");
            return -1;
        }
        int ret = DISK_SSCANF(buff, &info->nstatus[i]);
        // printf("%s %u %u\n", info->nstatus[i].device_name, info->nstatus[i].major_num, info->nstatus[i].minor_num);
        if (ret != DISK_SSCANF_NUM) {
            printf("DISK_SSCANF ret = %d\n", ret);
            return -1;
        } else {
            #if 0
            printf("%s\t%u\t%u\t%u\t%u\t%u\t%u\n", 
            info->nstatus[i].device_name, info->nstatus[i].major_num, info->nstatus[i].minor_num, 
            info->nstatus[i].read_complete, info->nstatus[i].read_merge, info->nstatus[i].read_sectors, info->nstatus[i].read_time_ms);
            #endif
        }
    }
    return 0;
}

int IOUsageRateInit(struct IoStatusInfo *info) {
    if (!info) return -1;
    info->fp = NULL;
    info->disk_num = 0;
    info->nstatus = NULL;
    info->io_usage_rate = NULL;
    info->read_speed = NULL;
    info->write_speed = NULL;

    /* 打开全局磁盘状态信息虚拟文件 */
    info->fp = fopen("/proc/diskstats", "r");
    if (info->fp == NULL) {
        // printf("Open /proc/diskstats file error, %s(%d)\n", strerror(errno), errno);
        printf("open /proc/diskstats fail\n");
        return -1;
    }

    char buff[4096];
    while (fgets(buff, sizeof(buff), info->fp) != NULL) {
        info->disk_num++;
    }
    // printf("info->disk_num = %d\n", info->disk_num);
    
    if (info->disk_num) {
        info->nstatus = (struct DiskStatus *)malloc(sizeof(struct DiskStatus) * info->disk_num);
        info->io_usage_rate = (float*)malloc(sizeof(float) * info->disk_num);
        info->read_speed = (DISK_STAT_ITEM_SIZE*)malloc(sizeof(DISK_STAT_ITEM_SIZE) * info->disk_num);
        info->write_speed = (DISK_STAT_ITEM_SIZE*)malloc(sizeof(DISK_STAT_ITEM_SIZE) * info->disk_num);
        if (!info->nstatus || !info->io_usage_rate || !info->read_speed || !info->write_speed) {
        } else {
            if (IOUsageReadOnce(info) == 0) {
                return 0;
            }
        }
    }

    fclose(info->fp);
    free(info->nstatus);
    free(info->io_usage_rate);
    free(info->read_speed);
    free(info->write_speed);
    info->fp = NULL;
    info->nstatus = NULL;
    info->io_usage_rate = NULL;
    info->read_speed = NULL;
    info->write_speed = NULL;
    return -1;
}

void IOUsageRateDeinit(struct IoStatusInfo *info) {
    if (!info) return;
    fclose(info->fp);
    free(info->nstatus);
    free(info->io_usage_rate);
    free(info->read_speed);
    free(info->write_speed);
}

int GetIOUsageRate(struct IoStatusInfo *info, int cross_time_ms) {
    if (!info || !info->fp) return -1;
    // prepare work
    struct DiskStatus *old_info = (struct DiskStatus *)malloc(sizeof(struct DiskStatus) * info->disk_num);
    if (!old_info) {
        return -1;
    }
    memcpy(old_info, info->nstatus, sizeof(struct DiskStatus) * info->disk_num);
    // read new one
    if (IOUsageReadOnce(info) != 0) {
        free(old_info);
        return -1;
    } else {
        // calculate
        for (int i = 0; i < info->disk_num; i++) {
            info->io_usage_rate[i] = (float)(info->nstatus[i].io_time_ms - old_info[i].io_time_ms)*100 / cross_time_ms;
            // 读写速度只有在扇区大小为 512Byte 时适用
            info->read_speed[i] = (info->nstatus[i].read_sectors - old_info[i].read_sectors)*512*1000/cross_time_ms;
            info->write_speed[i] = (info->nstatus[i].write_sectors - old_info[i].write_sectors)*512*1000/cross_time_ms;
        }
    }
    
    free(old_info);
    return 0;
}

